#include "ec11.h"
#include "fwapplication.h"
#include "log/logger.h"
#include "soc/soc_misc.h"

#define EC11_TIMER_MS 5
#define EC11_DELAY_CHECK_MS 5

#define EC11_EXTI_MIN_INTERVAL_MS 8
#define EC11_DIR_CHANGED_MIN_INTERVAL_MS 80

#define READ_IO_FILTER_MAX_COUNT 1

EC11Button::EC11Button()
{
    
}

EC11Button::EC11Button(GpioPortPin eca, GpioPortPin ecb)
{
    SetInfo(eca, ecb);
}

EC11Button::~EC11Button()
{
}

void EC11Button::SetInfo(GpioPortPin eca, GpioPortPin ecb)
{   
    m_APort = eca;
    m_BPort = ecb;
}

void EC11Button::SetScale(int value)
{
    m_scale = value;
}

int EC11Button::GetScale()
{
    return m_scale;
}

void EC11Button::SetDivChanged(int value)
{
    m_divChangedCount = value > 0? value : 1;
}

int EC11Button::GetDivChanged()
{
    return m_divChangedCount;
}

int EC11Button::GetCurrentPosition()
{
    return m_currentPos;
}

int EC11Button::GetCurrentAngle()
{
    if(m_scale <= 0)
        return 0;
    return (360 / m_scale) * m_currentPos;
}

void EC11Button::Start()
{
    if(!m_isInit)
    {
        __Init();
        m_isInit = true;
    }
}

void EC11Button::Stop()
{
    
}

void EC11Button::SetPostionZero()
{
    m_prevChangedDirMs = 0;
    m_currentPos = 0;
    HandleValue(1);
}

void EC11Button::SetValueChangedCallBack(const ValueChangedCallBack &&func)
{
    m_valueChangedCallBack = func;
}

void EC11Button::SetDirChangedCallBack(const RotationDirCallBack &&func)
{
    m_dirChanged = func;
}

std::string EC11Button::DirToString(DeviceDirection dir)
{
    switch (dir)
    {
    case DeviceDirection::CW:
        return "CW";
    case DeviceDirection::CCW:
        return "CCW";
    default:
        return "UN";
    }
}

int EC11Button::ReadValueFilter(const GpioPortPin &portPin)
{
    int value = -1;

    if(READ_IO_FILTER_MAX_COUNT <= 1)
    {
        value = SOC_Gpio::Read(portPin);
    }
    else
    {
        uint8_t lowCount = 0;
        uint8_t highCount = 0;
        for(int i = 0; i < READ_IO_FILTER_MAX_COUNT; i++)
        {
            if(SOC_Gpio::Read(portPin) == 1)
            {
                highCount++;
            }
            else 
            {
                lowCount++;
            }
        }

        if(highCount > lowCount)
        {
            value = 1;
        }
        else 
        {
            value = 0;
        }
    }

    return value;
}

void EC11Button::ReadValuePolling()
{
    int a, b;

    switch(m_pollState)
    {
    case PollingState::Idle:
    {
        a = ReadValueFilter(m_APort);
        
        if(m_aPValue == 1 && a == 0)
        {
            m_delayCurrentMs = 0;
            m_pollState = PollingState::Delay;
        }
    }
    break;
    case PollingState::Delay:
    {
        m_delayCurrentMs += EC11_TIMER_MS;
        if(m_delayCurrentMs >= EC11_DELAY_CHECK_MS)
        {
            auto b = ReadValueFilter(m_BPort);     
            HandleValue(b);
            m_pollState = PollingState::Idle;
        }
    }
    break;
    }
    
    //
    if(m_timeBaseCount >= m_divChangedCount)
    {
        m_timeBaseCount = 0;
    }
    if(m_timeBaseCount == 0)
    {
        if(m_preReportPos != m_currentPos)
        {
            if(m_valueChangedCallBack)
            {
                m_valueChangedCallBack(*this, m_preReportPos, m_currentPos);
                m_preReportPos = m_currentPos;
            }
        }
    }
    //后加
    m_timeBaseCount += EC11_TIMER_MS;

    m_aPValue = a;   
}

void EC11Button::HandleValue(int value)
{
    auto dir = DeviceDirection::None;

    dir = (value == 0)? DeviceDirection::CW : DeviceDirection::CCW;
    bool hasChanged = dir != m_lastDir;
    if(hasChanged)
    {
        //间隔不符合要求
        if(SOC_Misc::CurrentCpuMilliseconds() - m_prevChangedDirMs < EC11_DIR_CHANGED_MIN_INTERVAL_MS)
        {
            return;
        }
    }

    //先通知方向
    if(hasChanged)
    {
        if(m_dirChanged)
        {
            m_dirChanged(*this, dir);
        }
    }

    //确认变化，处理
    if(dir == DeviceDirection::CW)
    {
        m_currentPos++;
    }
    else
    {
        m_currentPos--;
    }

    m_lastDir = dir;
}

void EC11Button::__Init()
{
    m_timer.SetInterval(EC11_TIMER_MS);
    m_timer.SetElapsed([=](auto &timer){ReadValuePolling();});
    m_timer.Start();
}
